﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Commnications
{
    public class DefaultEasyComm<ReqHead> : EasyCommBase
    {
        protected IDataMessage _netMessage;
        protected virtual byte[] PackCommand(ReqHead command, byte[] cmd)
        {
            return cmd;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="send"></param>
        /// <param name="usePackHead"></param>
        /// <param name="station"></param>
        /// <returns>
        /// </returns>
        protected EasyResultT2<RequsetReply> Read(byte[] send, bool usePackHead, ReqHead station)
        {
            DateTime start = DateTime.Now;
            DateTime hasDataTime = DateTime.Now;
            List<byte> result = new List<byte>();
            var command = send;
            if (usePackHead)
                command = PackCommand(station, send);
            _context.DiscardInBuffer();
            _context.Send(command);

            var head = Receive(_netMessage?.HeadLength ?? 0, command, _netMessage);
            if (!head.IsSuccess)
            {
                return EasyResultT2<RequsetReply>.Create(head);
            }
            var len = -1;
            if (_netMessage?.HeadLength > 0)
                len = _netMessage.GetMessageLength(head.Data);
            var message = Receive(len, command, _netMessage);
            if (!message.IsSuccess)
            {
                return EasyResultT2<RequsetReply>.Create(message);
            }
            if (head.Data?.Length > 0)
            {
                result.AddRange(head.Data);
            }
            if (message.Data.Length > 0)
            {
                result.AddRange(message.Data);
            }
            if (!_netMessage.CheckHeadValid(command, result.ToArray()))
            {
                return new EasyResultT2<RequsetReply>(false) { Message = $"transid or command is not match." };
            }
            return new EasyResultT2<RequsetReply>(true, new RequsetReply(command, result.ToArray()));
        }
        protected EasyResultT2<byte[]> ReceiveTimeout(int timeout, byte[] command, EasyContext context, IDataMessage netMessage = null)
        {
            var buffer = new byte[context.ReceiveBufferSize];
            int receiveCount = 0;
            List<byte> result = new List<byte>();
            DateTime start = DateTime.Now;
            var isOk = false;
            Exception ex = null;
            while (true)
            {
                var bytesRead = context.Receive(buffer, buffer.Length);
                receiveCount += bytesRead;
                if (bytesRead > 0)
                {
                    start = DateTime.Now;
                    result.AddRange(buffer.Take(bytesRead));
                }
                if (netMessage?.CheckCompleteMessage(command, result.ToArray()) ?? false)
                {
                    isOk = true;
                    break;
                }
                var tsNodata = DateTime.Now.Subtract(start).TotalMilliseconds;
                if (tsNodata >= timeout)
                {
                    ex = new CommException($"After {tsNodata}ms not get data.");
                    break;
                }
            }
            return new EasyResultT2<byte[]>(isOk, result.ToArray()) { InnerException = ex };
        }
        #region Reveive Content        
        EasyResultT2<byte[]> Receive(int length, byte[] command, IDataMessage netMessage)
        {
            try
            {
                if (length == 0)
                {
                    return new EasyResultT2<byte[]>(true, new byte[0]);
                }
                else if (length == -1)
                {
                    return ReceiveTimeout(_context.ReadTimeout, command, _context, netMessage);
                }
                else
                {
                    var buffer = new byte[_context.ReceiveBufferSize];
                    int receiveCount = 0;
                    List<byte> result = new List<byte>();

                    while (receiveCount < length)
                    {
                        var len = length - receiveCount;
                        var bytesRead = _context.Receive(buffer, len);
                        receiveCount += bytesRead;
                        if (bytesRead > 0)
                            result.AddRange(buffer.Take(bytesRead));
                    }
                    return new EasyResultT2<byte[]>(receiveCount >= length, result.ToArray());
                }
            }
            catch (Exception ex)
            {
                return new EasyResultT2<byte[]>(new CommException($"{ex}"));
            }
        }
        #endregion
    }
}
